package gee

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// H alias of map string to interface
type H map[string]interface{}

// Context
// @Description: define a context which associate with request
type Context struct {
	// origin object
	Writer  http.ResponseWriter
	Request *http.Request

	// request info
	Path   string
	Method string
	Params map[string]string

	// response code
	StatusCode int

	// middleware
	handlers []HandlerFunc
	index    int

	// engine
	engine *Engine
}

// newContext
// @Description: only can see in package; generate a new context instance
// @param writer *http.ResponseWriter
// @param request *http.Request
// @return *Context
func newContext(writer http.ResponseWriter, request *http.Request) *Context {
	return &Context{
		Writer:  writer,
		Request: request,
		Path:    request.URL.Path,
		Method:  request.Method,
		index:   -1,
	}
}

func (c *Context) Next() {
	// 默认从第一个开始执行,若遇到中间有next,则接着向后执行,后面执行完后,再执行本fun的后续部分
	c.index++ // 保证每次调用Next都能接着继续向后执行
	s := len(c.handlers)
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

// PostForm
// @Description: get post param from request
// @receiver c
// @param key string
// @return string
func (c *Context) PostForm(key string) string {
	return c.Request.FormValue(key)
}

// Query
// @Description: find get param from request
// @receiver c
// @param key string
// @return string
func (c *Context) Query(key string) string {
	return c.Request.URL.Query().Get(key)
}

// Param
// @Description: find param from url pattern
// @receiver c *Context
// @param key string
// @return string
func (c *Context) Param(key string) string {
	value, _ := c.Params[key]
	return value
}

// Status
// @Description: set http status code
// @receiver c
// @param code int
func (c *Context) Status(code int) {
	c.StatusCode = code
	c.Writer.WriteHeader(code) // writer is value not a pointer
}

// SetHeader
// @Description: set header
// @receiver c
// @param key string
// @param value string
func (c *Context) SetHeader(key, value string) {
	c.Writer.Header().Set(key, value)
}

// String
// @Description: response string
// @receiver c
// @param code int
// @param format string
// @param values ...interface{}
func (c *Context) String(code int, format string, values ...interface{}) {
	c.Status(code)
	c.SetHeader("Content-Type", "text/plain")

	c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) HTML(code int, name string, data interface{}) {
	c.Status(code)
	c.SetHeader("Content-Type", "text/html")

	if err := c.engine.htmlTemplates.ExecuteTemplate(c.Writer, name, data); err != nil {
		c.Fail(http.StatusInternalServerError, err.Error())
	}
}

// JSON
// @Description: response json
// @receiver c
// @param code int
// @param obj interface{}
func (c *Context) JSON(code int, obj interface{}) {
	c.Status(code)
	c.SetHeader("Content-Type", "application/json")

	/*
		// resolution 1: json.Marshal/json.Unmarshal
		if bts, err := json.Marshal(obj); err != nil {//
			c.Writer.Write(bts)
		} else {
			http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
		}
	*/

	// resolution 2: json.Encoder/json.Decoder
	encoder := json.NewEncoder(c.Writer) // encode and write to the writer
	if err := encoder.Encode(obj); err != nil {
		http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
	}
}

// Bytes
// @Description: set response data bytes
// @receiver c
// @param code int
// @param bts []byte
func (c *Context) Bytes(code int, bts []byte) {
	c.Status(code)
	c.Writer.Write(bts)
}

func (c *Context) Fail(code int, err string) {
	c.String(code, err)
}
